package demo.mybatis;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @author : feixiang.li
 * @since : 2025-09-18 15:42
 */
public class MySqlSessionFactory {

    public static final String ip = "jdbc:mysql://rm-uf682626j42h0nn6ljo.mysql.rds.aliyuncs.com:3306/aigc?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    public static final String name = "jiema";
    public static final String password = "123qweQWE";

    public <T> T getMapper(Class<T> clazz) {

        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return new MapperInvocationHandler().invoke(proxy, method, args);
            }
        });
    }

    public static class MapperInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            String sql = createSql(method);
            try (Connection connection = DriverManager.getConnection(ip, name, password)) {
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof String) {
                        preparedStatement.setString(i + 1, (String) args[i]);
                    }
                    if (args[i] instanceof Integer) {
                        preparedStatement.setInt(i + 1, (Integer) args[i]);
                    }
                }
                ResultSet resultSet = preparedStatement.executeQuery();
                while (resultSet.next()) {
                    return parseReturn(method, resultSet);
                }
            }
            return null;
        }
    }

    public static Object parseReturn(Method method, ResultSet resultSet) throws Exception {
        Constructor<?> constructor = method.getReturnType().getConstructor();
        Object object = constructor.newInstance();
        for (Field field : method.getReturnType().getDeclaredFields()) {
            Object columnValue = null;
            if (field.getType() == String.class) {
                columnValue = resultSet.getString(field.getName());
            } else if (field.getType() == Integer.class) {
                columnValue = resultSet.getInt(field.getName());
            }
            field.setAccessible(true);
            field.set(object, columnValue);
        }
        return object;
    }

    static String createSql(Method method) {
        StringBuilder sql = new StringBuilder();
        sql.append("select ");
        for (String fieldName : getSelectList(method.getReturnType())) {
            sql.append(fieldName).append(",");
        }
        sql.deleteCharAt(sql.length() - 1);
        sql.append(" from ").append(getTableName(method.getReturnType()));
        sql.append(" where ").append(getSelectWhere(method));
        return sql.toString();
    }


    static String getSelectWhere(Method method) {
        Parameter[] parameters = method.getParameters();
        StringBuilder sql = new StringBuilder();
        for (Parameter parameter : parameters) {
            Param param = parameter.getAnnotation(Param.class);
            if (param != null) {
                sql.append(param.value()).append(" = ?,");
            }
        }
        sql.deleteCharAt(sql.length() - 1);
        return sql.toString();
    }

    static String getTableName(Class<?> clazz) {
        Table table = clazz.getAnnotation(Table.class);
        return table.name();
    }

    static List<String> getSelectList(Class<?> clazz) {
        List<String> list = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            // 如何获取其非驼峰格式的名称 比如 userName -> user_name
            list.add(CamelCaseConverter.camelToSnake(field.getName()));
        }
        return list;
    }
}
